﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

using System.Linq;
using MicroCenter.Models;

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

using Utils;

namespace MicroCenter.Services
{
    /// <summary>
    /// 进程守护
    /// </summary>
    [Injection(ServiceLifetime.Singleton), JobService]
    public class GuardianService
    {
        public readonly IWebHostEnvironment webHostEnvironment;

        public readonly LogService logService;

        public readonly MicroService microService;

        private readonly Dictionary<string, (ProcessHelper, ManualResetEvent)> runApps = new Dictionary<string, (ProcessHelper, ManualResetEvent)>();

        private readonly List<string> running = new List<string>();

        private readonly List<string> stopping = new List<string>();

        public readonly IWritableOptions<MicroCenterOptions> writableOptions;

        [AppRun]
        public void Run()
        {
            Task.Factory.StartNew(async () =>
            {
                await Task.Delay(2000);
                var autoRuns = writableOptions.Value.Autorun;
                var services = GetAllServicesWithStatus();
                foreach (var service in services)
                {
                    if (service.Status == 0 && service.IsAutoRun && service.HasRunService)
                        Run(service.ServiceName, 5000);
                }
            });
        }

        [Corn("0 * * * * ? *")]
        public void AutoRun()
        {
            if (running.Count > 0) return;
            var services = GetAllServicesWithStatus();
            foreach (var service in services)
            {
                if (service.Status == 0 && service.IsAutoRun && service.HasRunService)
                {
                    Run(service.ServiceName, 5000);
                }
            }
        }
        private string GetServiceDirectory()
        {
            var path = Path.Combine(webHostEnvironment.WebRootPath, ConfigConst.RUN_PATH);
            if (!Directory.Exists(path))
                Directory.CreateDirectory(path);
            return path;
        }

        public string GetEnvironment() => writableOptions.Value.Environment;

        /// <summary>
        /// 获取所有服务
        /// </summary>
        /// <returns></returns>
        public string[] GetAllServices()
        {
            return Directory.GetDirectories(GetServiceDirectory()).Select(Path.GetFileName).OrderBy(a => a).ToArray();
        }

        public ServiceStatusModel[] GetAllServicesWithStatus()
        {
            var autoRuns = writableOptions.Value.Autorun;
            return GetAllServices().Select(a => new ServiceStatusModel
            {
                ServiceName = a,
                Status = GetStatus(a),
                IsAutoRun = autoRuns.Contains("*") || autoRuns.Contains(a, StringComparer.OrdinalIgnoreCase),
                HasRunService = HasRunApp(a),
            }).ToArray();
        }

        /// <summary>
        /// 设置自动启动
        /// </summary>
        /// <param name="serviceName"></param>
        /// <param name="isAutoRun"></param>
        public void SetAutoRun(string serviceName, bool isAutoRun)
        {
            Check(serviceName);
            writableOptions.Update(options =>
            {
                if (isAutoRun)
                {
                    if (options.Autorun.Contains(serviceName)) return false;
                    options.Autorun = options.Autorun.Append(serviceName).ToArray();
                }
                else
                {
                    if (!options.Autorun.Contains(serviceName)) return false;
                    options.Autorun = options.Autorun.Where(a => !a.Equals(serviceName, StringComparison.OrdinalIgnoreCase)).ToArray();
                }
                return true;
            });
        }

        public void SetAllAutoRun(bool isAutoRun)
        {
            writableOptions.Update(options =>
            {
                if (isAutoRun)
                {
                    if (options.Autorun.Contains("*")) return false;
                    options.Autorun = options.Autorun.Append("*").ToArray();
                }
                else
                {
                    if (!options.Autorun.Contains("*")) return false;
                    options.Autorun = options.Autorun.Where(a => a != "*").ToArray();
                }
                return true;
            });
        }
        /// <summary>
        /// 创建服务
        /// </summary>
        /// <param name="serviceName"></param>
        public void CreateService(string serviceName)
        {
            var services = GetAllServices();
            if (services.Contains(serviceName)) throw Error.DefaultError("服务已存在");
            _ = Directory.CreateDirectory(Path.Combine(GetServiceDirectory(), serviceName));
        }

        public void TryStopService(string serviceName)
        {
            Check(serviceName);
            var status = GetStatus(serviceName);
            if (status != 0)
            {
                Kill(serviceName, 2000);
            }
        }

        /// <summary>
        /// 启动权限
        /// </summary>
        /// <param name="serviceName"></param>
        public void Empower(string serviceName)
        {
            var directoryName = Path.Combine(GetServiceDirectory(), serviceName);
            if (ProcessHelper.IsLinux())
            {
                foreach (var file in Directory.GetFiles(directoryName))
                {
                    if (string.IsNullOrEmpty(Path.GetExtension(file)))
                    {
                        ProcessHelper.RunCmd("chmod u+x " + file, "/usr/bin/").Start().GetAwaiter().GetResult();
                    }
                }
            }
        }
        /// <summary>
        /// 删除服务
        /// </summary>
        /// <param name="serviceName"></param>
        /// <param name="force"></param>
        public void DeleteService(string serviceName, bool force)
        {
            Check(serviceName);
            var status = GetStatus(serviceName);
            if (status != 0)
            {
                if (!force)
                {
                    throw Error.DefaultError("服务正在运行");
                }
                Kill(serviceName, 1000);
            }
            Directory.Delete(Path.Combine(GetServiceDirectory(), serviceName), true);
            writableOptions.Update(options =>
            {
                if (!options.Autorun.Contains(serviceName)) return false;
                options.Autorun = options.Autorun.Where(a => !a.Equals(serviceName, StringComparison.OrdinalIgnoreCase)).ToArray();
                return true;
            });
        }
        public void Run(string serviceName, int millisecondsTimeout)
        {
            Check(serviceName);
            var status = GetStatus(serviceName);
            if ((status & 2) > 0)
            {
                throw Error.DefaultError($"服务{(status == 1 ? "启动" : "停止")}中，请稍后...");
            }
            if ((status & 1) > 0)
            {
                throw Error.DefaultError("服务已启动");
            }
            var workingDirectory = Path.Combine(GetServiceDirectory(), serviceName);
            var process = ProcessHelper.RunCmd(BuilderCmdStr(serviceName, workingDirectory), workingDirectory);
            string environment = GetEnvironment();
            if (!string.IsNullOrWhiteSpace(environment))
            {
                process.GetProcess().StartInfo.Environment.Add(ConfigConst.ENVIRONMENT_NAME, environment);
            }
            ManualResetEvent manualResetEvent = new ManualResetEvent(false);
            runApps.Add(serviceName, (process, manualResetEvent));
            running.Add(serviceName);
            logService.ClearCurrentDay(serviceName);
            process.Start(msg =>
            {
                if (!string.IsNullOrEmpty(msg))
                {
                    logService.Write(msg, serviceName);
                    if (msg.Contains("Application started"))
                    {
                        running.Remove(serviceName);
                        manualResetEvent.Set();
                    }
                }
            }, () =>
            {
                runApps.Remove(serviceName);
                running.Remove(serviceName);
                manualResetEvent.Set();
            });
            if (!manualResetEvent.WaitOne(millisecondsTimeout))
            {
                process.Kill();
                throw Error.DefaultError("启动超时");
            }
            if (!runApps.ContainsKey(serviceName))
                throw Error.DefaultError("启动失败");
        }

        public void Kill(string serviceName, int millisecondsTimeout)
        {
            Check(serviceName);
            var status = GetStatus(serviceName);
            if ((status & 2) > 0)
            {
                throw Error.DefaultError($"服务{(status == 1 ? "启动" : "停止")}中，请稍后...");
            }
            if ((status & 1) == 0)
            {
                throw Error.DefaultError("服务未启动");
            }
            if (runApps.TryGetValue(serviceName, out (ProcessHelper process, ManualResetEvent manualResetEvent) item))
            {
                Stop(item.process, item.manualResetEvent, serviceName, millisecondsTimeout);
            }
            else
            {
                Stop(serviceName);
            }
            Thread.Sleep(1000);
        }
        public void Check(string serviceName)
        {
            var services = GetAllServices();
            if (!services.Contains(serviceName))
            {
                throw Error.DefaultError("服务不存在");
            }
        }


        private string BuilderCmdStr(string serviceName, string workingDirectory)
        {
            if (!File.Exists(Path.Combine(workingDirectory, ProcessHelper.IsWindows() ? serviceName + ".exe" : serviceName)))
            {
                string serviceDLL = serviceName + ".dll";
                if (!File.Exists(Path.Combine(workingDirectory, serviceDLL)))
                {
                    throw Error.DefaultError("缺少启动文件");
                }
                return $"dotnet {serviceDLL}";
            }
            if (ProcessHelper.IsWindows())
                return $"{serviceName}.exe";
            else
                return $"./{serviceName}";
        }

        private bool HasRunApp(string serviceName)
        {
            var workingDirectory = Path.Combine(GetServiceDirectory(), serviceName);
            if (!File.Exists(Path.Combine(workingDirectory, ProcessHelper.IsWindows() ? serviceName + ".exe" : serviceName)))
            {
                string serviceDLL = serviceName + ".dll";
                if (!File.Exists(Path.Combine(workingDirectory, serviceDLL)))
                {
                    return false;
                }
            }
            return true;
        }
        private Process GetProcessesByName(string serviceName)
        {
            var ps = Process.GetProcessesByName(serviceName);
            if (ps.Length > 0) return ps[0];

            string cmd = $"ps -ef|grep {serviceName}.dll |grep grep -v |awk '{{print $2}}'";

            string strId = ProcessHelper.RunCmd(cmd).StartWaitResult().GetAwaiter().GetResult();
            if (!strId.IsNullOrEmpty() && int.TryParse(strId, out int id))
            {
                return Process.GetProcessById(id);
            }
            return null;

        }
        private int RunStatus(string serviceName)
        {
            if (runApps.ContainsKey(serviceName))
            {
                return 1;
            }
            if (GetProcessesByName(serviceName) != null)
            {
                return 1;
            }
            return 0;
        }
        /// <summary>
        /// 状态
        /// </summary>
        /// <param name="serviceName"></param>
        /// <returns> 0 未启动，1 已启动，0|2 已启中 ，1|2 停止中，4 在线</returns>
        private int GetStatus(string serviceName)
        {
            if (running.Contains(serviceName))
            {
                return 2;
            }
            if (stopping.Contains(serviceName))
            {
                return 1 | 2;
            }
            var regs = microService.GetRegistries();
            if (regs?.Any(a => a.ServiceName == serviceName) ?? false)
            {
                return 4 | RunStatus(serviceName);
            }
            return RunStatus(serviceName);
        }


        private void Stop(string serviceName)
        {
            if (runApps.ContainsKey(serviceName))
                runApps.Remove(serviceName);
            var p = GetProcessesByName(serviceName);
            if (p != null)
                p.Kill();
        }

        private void Stop(ProcessHelper process, ManualResetEvent manualResetEvent, string serviceName, int millisecondsTimeout)
        {
            stopping.Add(serviceName);
            process.Kill();
            manualResetEvent.Reset();
            if (!manualResetEvent.WaitOne(millisecondsTimeout))
            {
                if (ProcessHelper.IsWindows())
                {
                    var p = GetProcessesByName(serviceName);
                    if (p != null)
                        p.Kill();
                }
                else
                {
                    ProcessHelper.RunCmd($"kill -9 {process.GetProcess().Id}");
                }

            }
            stopping.Remove(serviceName);
            runApps.Remove(serviceName);

        }
    }
}
